﻿using System;
using System.Linq;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Castle.ActiveRecord.Queries;
using NHibernate;
using NHibernate.Transform;

namespace Pampa.MVC.ActiveRecord
{
    public class PampaSqlQuery<T> : HqlBasedQuery
    {
        private readonly ISessionFactoryHolder holder;

        public PampaSqlQuery(string query) : base(typeof(T), QueryLanguage.Sql, query)
        {
            holder = ActiveRecordMediator.GetSessionFactoryHolder();
        }

        public PampaSqlQuery(string query, params object[] positionalParameters) : base(typeof(T), QueryLanguage.Sql, query, positionalParameters)
        {
            holder = ActiveRecordMediator.GetSessionFactoryHolder();
        }

        public int ExecuteUpdate(int? timeout = null)
        {
            return ExecuteUpdateQuery(timeout);
        }

        public T[] Execute(int? timeout = null)
        {
            return ExecuteQuery(0, 0, timeout);
        }

        public T[] Execute(int skipCount, int takeCount, int? timeout = null)
        {
            return ExecuteQuery(skipCount, takeCount, timeout);
        }

        private int ExecuteUpdateQuery(int? timeout = null)
        {
            Type rootType = this.RootType;
            ISession session = holder.CreateSession(rootType);

            try
            {
                return ExecuteUpdate(session, timeout);
            }
            catch (Exception ex)
            {
                holder.FailSession(session);
                throw new ActiveRecordException("Could not perform ExecuteQuery for " + rootType.Name, ex);
            }
            finally
            {
                holder.ReleaseSession(session);
            }
        }

        private T[] ExecuteQuery(int skipCount, int takeCount, int? timeout = null)
        {
            Type rootType = this.RootType;
            ISession session = holder.CreateSession(rootType);

            try
            {
                return Execute(session, skipCount, takeCount, timeout);
            }
            catch (Exception ex)
            {
                holder.FailSession(session);
                throw new ActiveRecordException("Could not perform ExecuteQuery for " + rootType.Name, ex);
            }
            finally
            {
                holder.ReleaseSession(session);
            }
        }

        private int ExecuteUpdate(ISession session, int? timeout = null)
        {
            var query = CreateQuery(session);

            if(timeout.HasValue)
            {
                query.SetTimeout(timeout.Value);
            }

            return query.ExecuteUpdate();
        }

        private T[] Execute(ISession session, int skipCount, int takeCount, int? timeout = null)
        {
            var query = CreateQuery(session).SetResultTransformer(Transformers.AliasToBean(typeof(T)));

            if (timeout.HasValue)
            {
                query.SetTimeout(timeout.Value);
            }

            if (skipCount > 0)
            {
                query.SetFirstResult(skipCount);
            }

            if (takeCount > 0)
            {
                query.SetFetchSize(takeCount);
            }

            var list = query.List<T>();
            return list.ToArray();
        }
    }
}
